// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import "unsafe"

func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
	// nil指针始终正确对齐（#47430）。
	if p == nil {
		return
	}

	// 检查（*[n]elem）（p）是否正确对齐。
	// 注意，如果指针指向的类型本身不包含指针，那么我们允许未对齐的指针。见第37298期。
	// TODO（mdempsky）：fieldAlign呢？
	if elem.ptrdata != 0 && uintptr(p)&(uintptr(elem.align)-1) != 0 {
		throw("checkptr: misaligned pointer conversion")
	}

	// 检查（*[n]elem）（p）是否跨越多个堆对象。
	// TODO（mdempsky）：修复#46938，这样我们就不需要担心这里的溢出。
	if checkptrStraddles(p, n*elem.size) {
		throw("checkptr: converted pointer straddles multiple allocations")
	}
}

// checkptrStraddles报告由ptr寻址的第一个大小字节是否跨越多个Go分配。
func checkptrStraddles(ptr unsafe.Pointer, size uintptr) bool {
	if size <= 1 {
		return false
	}

	// 检查add（ptr，size-1）是否不会溢出。这避免了产生非法指针值的风险（假设ptr是合法的）。
	if uintptr(ptr) >= -(size - 1) {
		return true
	}
	end := add(ptr, size-1)

	// TODO（mdempsky）：检测[ptr，end]何时包含Go分配，
	// 但ptr和end point本身都不包含。

	return checkptrBase(ptr) != checkptrBase(end)
}

func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
	if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
		throw("checkptr: pointer arithmetic computed bad pointer value")
	}

	// 检查如果计算出的指针p指向堆
	// 对象，那么其中一个原始指针必须指向同一个对象。
	base := checkptrBase(p)
	if base == 0 {
		return
	}

	for _, original := range originals {
		if base == checkptrBase(original) {
			return
		}
	}

	throw("checkptr: pointer arithmetic result points to invalid allocation")
}

// checkptrBase返回包含
// 地址p.
// 
// 重要的是，如果p1和p2指向同一个变量，则
// checkptrBase（p1）=checkptrBase（p2）。然而，逆/逆
// 不一定是真的，因为分配可以有尾随填充，
// 并且多个变量可以打包到一个分配中。
func checkptrBase(p unsafe.Pointer) uintptr {
	// 堆栈
	if gp := getg(); gp.stack.lo <= uintptr(p) && uintptr(p) < gp.stack.hi {
		// TODO（mdempsky）：遍历堆栈以识别
		// 特定的堆栈帧，甚至是p 
		// 指向的堆栈对象。
		// 
		// 同时，使用“1”作为伪地址来表示堆栈。这在
		// 所有平台上都是无效地址，因此保证它与我们可能返回的任何地址都是不同的
		// 地址。
		return 1
	}

	// 堆（由于#35068，必须在堆栈后检查）
	if base, _, _ := findObject(uintptr(p), 0, 0); base != 0 {
		return base
	}

	// 数据或bss 
	for _, datap := range activeModules() {
		if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
			return datap.data
		}
		if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss {
			return datap.bss
		}
	}

	return 0
}
